package main

import (
	"encoding/csv"
	"fmt"
	"gitee.com/libbylg/pkgr/repo"
	"os"
	"sort"
	"strings"
)

// CmdSelectionOptions 定义了 list 子命令的所有可能选型
type CmdSelectionOptions struct {
	Filter     *repo.ModuleLabelFilter
	Orders     []string
	Writeback  bool
	Separator  string
	ShowHeader bool
	SortBy     string
}

// NewCmdSelectionOptions 识别命令行选项.
// pkgr selection [PATTERN] [--regex] [--plan PLAN-FILE] [--view PLAN-VIEW] [--order TAG:ORDER-FILE ...]
func NewCmdSelectionOptions(argv []string) (*CmdSelectionOptions, error) {
	if len(argv) < 2 {
		return nil, fmt.Errorf("the required parameter is missing, try -h for help")
	}

	// 初始化默认值
	opt := &CmdSelectionOptions{
		Filter:     repo.NewModuleLabelFilter(),
		Orders:     make([]string, 0, 3),
		Writeback:  false,
		Separator:  "",
		ShowHeader: true,
		SortBy:     "",
	}

	// 读取选项
	for index := 2; index < len(argv); index++ {
		// 是否按照正则表达式的方式去匹配
		if argv[index] == "--regex" {
			opt.Filter.Regex = true
			continue
		}

		// 指定 plan file 场景
		if argv[index] == "--plan" {
			if len(opt.Filter.PlanFile) != 0 {
				return nil, fmt.Errorf("the install plan file has been specified with `%v'", opt.Filter.PlanFile)
			}

			if index+1 >= len(argv) {
				return nil, fmt.Errorf("the option `%v' is missing value", argv[index])
			}

			index++
			opt.Filter.PlanFile = argv[index]
			continue
		}

		// 指定 plan env 场景
		if argv[index] == "--view" {
			if len(opt.Filter.PlanView) != 0 {
				return nil, fmt.Errorf("the plan env has been specified with `%v'", opt.Filter.PlanView)
			}

			if index+1 >= len(argv) {
				return nil, fmt.Errorf("the option `%v' is missing value", argv[index])
			}

			index++
			opt.Filter.PlanView = argv[index]
			continue
		}

		if argv[index] == "--devrepo" {
			if opt.Filter.WhichRepo != "" {
				continue
			}
			opt.Filter.WhichRepo = "devrepo"
			continue
		}

		if argv[index] == "--pkgrepo" {
			if opt.Filter.WhichRepo != "" {
				continue
			}
			opt.Filter.WhichRepo = "pkgrepo"
			continue
		}

		// 指定选型清单
		if argv[index] == "--order" {
			if index+1 >= len(argv) {
				return nil, fmt.Errorf("the option `%v' is missing value", argv[index])
			}

			index++
			opt.Orders = append(opt.Orders, argv[index])
			continue
		}

		// 输出是否排序
		if argv[index] == "--sort" {
			opt.SortBy = "label"
			continue
		}

		// 是否需要回写
		if argv[index] == "--writeback" {
			opt.Writeback = true
			continue
		}

		// 不显示表头
		if argv[index] == "--no-header" {
			opt.ShowHeader = false
			continue
		}

		// 指定输出分隔符（CSV）
		if argv[index] == "--separator" || argv[index] == "-s" {
			if index+1 >= len(argv) {
				return nil, fmt.Errorf("the option `%v' is missing value", argv[index])
			}

			index++
			opt.Separator = argv[index]
			continue

		}

		// 遇到无法识别的选项
		if strings.HasPrefix(argv[index], "-") {
			return nil, fmt.Errorf("unsupported options `%v'", argv[index])
		}

		// 识别 PATTERN 参数
		if len(opt.Filter.Pattern) == 0 {
			opt.Filter.Pattern = argv[index]
			continue
		}

		return nil, fmt.Errorf("unsupported option `%v'", argv[index])
	}

	// 整理
	opt.Filter.Tidy("pkgrepo")

	if opt.Separator == "" {
		opt.Separator = "|"
	}

	return opt, nil
}

type SelectionResult struct {
	Descriptor *repo.ModuleVersionDescription
	Selection  *repo.SelectionItem
	Order      *repo.SelectionOrder
	Reason     string
	Result     string
}

func CmdSelection(argv []string) int {
	// 加载选项
	opt, err := NewCmdSelectionOptions(argv)
	if err != nil {
		ERROR("Load option for `%v' failed: %v", "lectotype", err)
		return 1
	}

	// PKGREPO 必须存在
	if !repo.PathIsDir(repo.PKGREPO) {
		ERROR("The environment variable `$PKGREPO' is missing")
		return 1
	}

	// 加载所有的选型清单
	selectionOrderList := make([]*repo.SelectionOrder, 0, len(opt.Orders))
	orderTagMaps := make(map[string]*repo.SelectionOrder)
	orderFileMaps := make(map[string]*repo.SelectionOrder)
	for _, orderString := range opt.Orders {
		secs := strings.SplitN(orderString, "=", 2)
		if len(secs) != 2 {
			ERROR("The format of the order argument is invalid: `%v'", orderString)
			return 1
		}

		tag := secs[0]
		file := secs[1]

		// tag 与 file 必须一对一绑定
		if order, exist := orderTagMaps[tag]; exist {
			ERROR("The order tag `%v' has been bound with the order file `%v'", tag, order.File)
			return 1
		}

		// tag 与 file 必须一对一绑定
		if order, exist := orderFileMaps[file]; exist {
			ERROR("The order file `%v' has been bound with the order tag `%v'", file, order.Tag)
			return 1
		}

		order, err := repo.ReadSelectionOrder(tag, file)
		if err != nil {
			ERROR("Load the selection order file `%v' failed: %v", file, err)
			return 1
		}

		selectionOrderList = append(selectionOrderList, order)
		orderTagMaps[tag] = order
		orderFileMaps[file] = order
	}

	// 确定需要选型的范围
	moduleLabelList, err := repo.SelectModuleLabels(opt.Filter)
	if err != nil {
		ERROR("Filter modules failed: %v\n", err)
		return 1
	}

	if opt.SortBy == "label" {
		sort.Slice(moduleLabelList[:], func(i, j int) bool {
			return moduleLabelList[i].String() < moduleLabelList[j].String()
		})
	}

	// 保存选型结果的映射
	selectionResultMapping := make(map[string]*SelectionResult)
	for _, moduleLabel := range moduleLabelList {
		// 读取模块版本描述文件
		descriptor, err := repo.ReadModuleVersionDescription(moduleLabel.String())
		if err != nil {
			selectionResultMapping[moduleLabel.String()] = &SelectionResult{
				Descriptor: descriptor,
				Selection:  nil,
				Order:      nil,
				Reason:     "LOAD-VERSION-FILE-FAILED",
				Result:     "ERROR", // 出错
			}
			continue
		}

		//// 如果不是开源软件，不需要做选型
		//if descriptor.ImportAs != "opensource" {
		//	descriptor.TrainVersion = "NA"
		//
		//	// 没匹配上
		//	selectionResultMapping[moduleLabel.String()] = &SelectionResult{
		//		Descriptor: descriptor,
		//		Selection:  nil,
		//		Order:      nil,
		//		Reason:     "NO-OPENSOURCE-SOFTWARE",
		//		Result:     "SKIP", // 跳过
		//	}
		//	continue
		//}

		//// 按照优先级顺序做选型
		//orderIndex, selItem, matched := repo.SelectionMatching(descriptor.TrainModuleName, descriptor.TrainModuleVersion, selectionOrderList)
		//if !matched {
		//	// 识别时选型更新还是新选型匹配
		//	reasonText := "CAN-NOT-MATCH-ANY-ORDER" // 选型成功
		//	if descriptor.TrainVersion != "" {
		//		reasonText = fmt.Sprintf("LOSS-MATCH(%v)", descriptor.TrainVersion) // 选型信息丢失
		//	}
		//
		//	// 更新到 descriptor 中去
		//	descriptor.TrainVersion = ""
		//
		//	// 没匹配上
		//	selectionResultMapping[moduleLabel.String()] = &SelectionResult{
		//		Descriptor: descriptor,
		//		Selection:  nil,
		//		Order:      nil,
		//		Reason:     reasonText,
		//		Result:     "FAIL",
		//	}
		//} else {
		//	// 选型成功
		//	matchedOrder := selectionOrderList[orderIndex]
		//
		//	// 识别时选型更新还是新选型匹配
		//	reasonText := "NEW-MATCHED" // 选型成功
		//	if descriptor.TrainVersion != "" && descriptor.TrainVersion != matchedOrder.Tag {
		//		reasonText = fmt.Sprintf("MATCHED-AND-UPDATE(%v)", descriptor.TrainVersion) // 选型成功但变更
		//	}
		//
		//	// 更新到 descriptor 中去
		//	descriptor.TrainVersion = matchedOrder.Tag
		//
		//	// 保存选型结果
		//	selectionResultMapping[moduleLabel.String()] = &SelectionResult{
		//		Descriptor: descriptor,
		//		Selection:  selItem,
		//		Order:      matchedOrder,
		//		Reason:     reasonText,
		//		Result:     "OK",
		//	}
		//}

		continue
	}

	// 输出选型结果
	writer := csv.NewWriter(os.Stdout)
	writer.Comma = rune(opt.Separator[0])

	// 输出标题
	titles := []string{"#SELECTION", "LABEL", "IMPORTAS", "RESULT", "TRAINVERSION", "TRAINMODULENAME", "TRAINMODULEVERSION", "FAILREASON"}
	if opt.ShowHeader {
		writer.Write(titles)
		writer.Flush()
	}

	// 输出数据列
	for _, moduleLabel := range moduleLabelList {
		selResult, _ := selectionResultMapping[moduleLabel.String()]

		records := make([]string, len(titles))
		records[0] = "SELECTION"
		records[1] = moduleLabel.String()
		//records[2] = selResult.Descriptor.ImportAs
		records[3] = selResult.Result
		//records[4] = selResult.Descriptor.TrainVersion
		//records[5] = selResult.Descriptor.TrainModuleName
		//records[6] = selResult.Descriptor.TrainModuleVersion
		records[7] = selResult.Reason

		writer.Write(records)
		writer.Flush()
	}

	// 回写
	if opt.Writeback {
		for _, moduleLabel := range moduleLabelList {
			selResult, _ := selectionResultMapping[moduleLabel.String()]
			err := repo.WriteModuleVersionDescription(selResult.Descriptor)
			if err != nil {
				ERROR("Writeback the Selection result for `%v' failed: %v", moduleLabel.String(), err)
				continue
			}
		}
	}

	return 0
}
